/**
Company: Sarkar software technologys
WebSite: http://www.sarkar.cn
Author: yeganaaa
Date : 11/23/17
Time: 9:03 PM
 */

package Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core

import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Extensions.cloneObjects
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Extensions.executeSelectQuery
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.Extensions.simpleWhereCondition
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.PipeLine.Subjects.GenerateDeleteTaskSubject
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.PipeLine.Subjects.GenerateInsertTaskSubject
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryBuilderExtensions.SelectQueryExpression.*
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.Common.And
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.Common.Where
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.Common.WhereItemCondition
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.ISelectQueryExpression
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.Select.*
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.IDataContext
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.clonedPipeLine
import Cn.Sarkar.EntityDream.CoreEngine.RDBMS.execute
import java.util.*

fun <TABLE : IDBTable, ENTITY : IDBEntity> QueriableCollection<ENTITY>.query(queryAction: QueriableCollection<ENTITY>.(table: TABLE) -> Unit) = this.apply { queryAction(table as TABLE) }

class QueriableCollection<ENTITY : IDBEntity>(override var Context: IDataContext, override var table: IDBTable, private var where: WhereItemCondition? = null, override var ItemGenerator: () -> ENTITY) : LinkedList<ENTITY>(), ISelectQueryExpression by table.SelectQuery, IQueriableCollection<ENTITY> {

    override val clonedSelect: Select get() = Select.cloneObjects(Context)
    override val clonedFrom: From get() = From.cloneObjects(Context)
    override val clonedWhere: Where? get() = Where?.cloneObjects(Context)
    override val clonedGroupBy: GroupBy? get() = GroupBy?.cloneObjects(Context)
    override val clonedHaving: Having? get() = Having?.cloneObjects(Context)
    override val clonedOrderby: OrderBy? get() = OrderBy?.cloneObjects(Context)
    override var cached: Boolean = false

    /**
     * دەرىجىسى، يەنى ئەگەر بىز نۆۋەتتىكى توپلامدىن Where شەرتىنى ئۆزگەرتسەك ئۇ ھالدا نۆەتتىكى توپلامنىڭ Where شەرتى بىۋاستە ئۆزگىرىپ كىتىدۇ،
     * ئۇنداق بولغاندا شەرتى ئۆزگىرىپ كەتكەن ساندان ئاستىدىكىلا غول توپلامنى بىز باشقا ئىشقا ئىشلەتسەك، مەسىلەن ھىچقانداق شەرىت قويمايلا forEach بىلەن ئايلاندۇرساق
     * ئاۋۇ بىشىدا ئۆزگىرىپ كەتكەن شەرىتنى ساندانغا يۈرگۈزۈپ ئۇچۇرلارنىڭ ئەينەنسىزلىكىگە تەسىر يەتكۈزىدۇ،
     * بىز نۆۋەتتىكى توپلامغا where شەرىت قويۇپ ئۇنى يۈرگۈزۈپ چىقارغان نەتىجىسى يەنە توپلام بولغان ئەھۋالدا يىڭىدىن 0 باشلاپ توپلام ئىنىقلاشتىن ساقلىنىش ئۈچۈن نۆۋەتتىكى توپلامنى قايتا ئىشلەتكىلى بولسىلا ئىشلەتكىلى بولىدىغان ھالەتتە لاھىيەلىدۇق،
     * بۇنداق بولغاندا سانداننىڭ ئاستىدىكىلا غول توپلامدىن قاتمۇقات where شەرىپ قويۇپ ھاسىل قىلىنغان قاتمۇقات توپلاملارنى ئىككىگە ئايرىپ قوغداش كىرەك، كەينىدىكى توپلامنى خالىغانچە ئۆزگەرتسەك سانداننىڭ غول قىسمىدىكىسىگە تەسىر قىلماسلىقى كىرەك،
     * ئاستىدىكى بۇ خاسلىق دەل توپلامنىڭ قانچىنچى دەرىجىدە ئىكەنلىكىنى بىلدۈرىدۇ،
     * ئەگەر بۇ توپلام غول توپلام بولسا ئۇنداقتا قىممىتى 0 بولىدۇ، ئەگەر بىر دانە where دىن كىلىپ چىققان توپلام بولسا قىممىتى 1 بولىدۇ، ئاخىرىغا ئۇلاپ يەنە بىر نەتىجە چىقىرىلغان توپلام بولسا قىممىتى 2 بولىدۇ،
     * بىز مۇشۇ قىممەتلەرگە ئاساسەن قايسى توپلامنى قايسى ۋاقىتتا ئايرىپ كىلونلاشنىڭ كىرەكلىكىگە ھۆكۈم قىلىمىز。
     */
    override var Level: Int = 0

    override fun ValuesCacheItem.toEntity() : ENTITY = ItemGenerator().apply { values = this@toEntity; FromDB = true }

    init {
        /**
         * ئەگەر نۆۋەتتىكى ئوبىيكىت بىرگە كۆپ ماسلىق مۇناسىۋەت تۈپەيلى كىلىپ چىققان ئوبىيكىت بولسا
         * يەنە قايتىدىن بارلىق ئوبىيكىتلار ئىچىدىن ئىزدەشتىن ساقلىنىش ئۈچۈن where غا بىر شەرىت بىرىلىدۇ ۋە شۇ شەرىت ساقلاپ قىلىنىدۇ.
         * مەسىلەن
         * select xxxx from xxxx where xxxx AND {OneToManyObj}.{PrimaryKeyColumn} = {xxxx}
         */
        if (where != null) {
            if (this.Where == null) this.Where = Cn.Sarkar.EntityDream.CoreEngine.RDBMS.Core.QueryExpressionBlocks.Common.Where(where!!)
            else this.Where!!.condition = And(this.Where!!.condition, where!!)
        }
    }

    /**
     * يەرلىكتىكى سانداندىن ئۇچۇر يۈكلەيدۇ
     */
    fun loadFromDb() {
        if (cached) return
        super.clear()
        val result = Context.executeSelectQuery(this)
        result.forEach { super.add(it.toEntity()) }
        cached = true
    }

    override fun iterator(): MutableIterator<ENTITY> {
        loadFromDb()
        return super.iterator()
    }



    override val size: Int
        get() {

            val key = table.PrimaryKey.columns.first()
            val size = this count key
            return if (cached) super.size + size else size
        }


    override fun add(element: ENTITY): Boolean {
        val cpl = Context.clonedPipeLine
        val result = Context.execute(cpl, GenerateInsertTaskSubject(Context, table, element))
        return super.add(element)
    }

    override fun remove(element: ENTITY): Boolean {
        var whereCondition = where { element.simpleWhereCondition }
        if (where != null) whereCondition.andWhere { where!! }

        val subject = GenerateDeleteTaskSubject(element, whereCondition.Where!!)
        val result = Context.execute(Context.clonedPipeLine, subject)

        return super.remove(element)
    }

    override fun first() : ENTITY = Context.executeSelectQuery(this take 1)[0].toEntity()
    override fun firstOrNull(): ENTITY? = if (this.isEmpty()) null else first()
    override fun last() : ENTITY = Context.executeSelectQuery(this take 1 orderByDesc table.PrimaryKey.columns.first())[0].toEntity()
    override fun lastOrNull(): ENTITY? = if (this.isEmpty()) null else last()
    override fun single() = if (size == 1) first() else if (size > 1 )throw Exception("contains more items, بىردىن ئارتۇق ئەزا بار، 已存在多个元素") else throw Exception("No elements, ئىلىمىنىت يوق، 没有元素")
    override fun singleOrNull() = if (this.isEmpty()) null else single()
    override fun toArray(): Array<Any> {
        loadFromDb()
        return super.toArray()
    }
    override fun <T : Any?> toArray(a: Array<out T>?): Array<T> {
        loadFromDb()
        return super.toArray(a)
    }
}

